<!--

    Copyright (c) 2012-2018 Red Hat, Inc.
    This program and the accompanying materials are made
    available under the terms of the Eclipse Public License 2.0
    which is available at https://www.eclipse.org/legal/epl-2.0/

    SPDX-License-Identifier: EPL-2.0

    Contributors:
      Red Hat, Inc. - initial API and implementation

-->
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta name="mobile-web-app-capable" content="yes">
    <title>Eclipse Che</title>
    <link rel="shortcut icon" href="/_app/favicon.ico"/>
    <link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet" type="text/css"/>
    <script type="text/javascript" language="javascript">

        /**This parameter is needed to define sdk mode.*/
        window.sdk = 1;

        /**
         * Base IDE object
         */

        window.IDE = {};

        /**
         * Initial configuration
         */

        window.IDE.config = {
            "restContext": "/api",
            "websocketContext": "/websocket"
        };

        /**
         * Event handlers
         */

        window.IDE.eventHandlers = {};

        window.IDE.eventHandlers.initializationFailed = function (message) {
            if (message) {
                var err = new Error(message);
                window.alert(err.stack);
            } else {
                window.alert("Unable to initialize IDE");
            }
        };

        var Loader = new function() {

            var keycloak;

            /*
             * Load keycloak settings
             */
            this.loadKeycloakSettings = function() {
                var msg = "Cannot load keycloak settings. This is normal for single-user mode.";
                try {
                    if (window.parent && window.parent['_keycloak']) {
                        window['_keycloak'] = window.parent['_keycloak'];
                        Loader.startLoading();
                        return;
                    }
                } catch (e) {
                    // access to parent frame can be forbidden from the inner iframe,
                    // in case if IDE.html would be loaded(for example sidecar)
                    // from a different domain than parent frame.
                    console.error(msg, e);
                }
                
                try {
                    var request = new XMLHttpRequest();

                    request.onerror = request.onabort = function() {
                        console.error(msg);
                        Loader.startLoading();
                    };

                    request.onload = function() {
                        if (request.status == 200) {
                            Loader.injectKeycloakScript(JSON.parse(this.responseText));
                        } else {
                            console.error(msg);
                            Loader.startLoading();
                        }
                    };

                    var url = "/api/keycloak/settings";
                    request.open("GET", url, true);
                    request.send();
                } catch (e) {
                    console.error(msg, e);
                    Loader.startLoading();
                }
            };
            
            /*
             * Injects keycloak javascript
             */
            this.injectKeycloakScript = function(keycloakSettings) {
                var script = document.createElement("script");
                script.type = "text/javascript";
                script.language = "javascript";
                script.async = true;
                script.src = keycloakSettings['che.keycloak.js_adapter_url'];

                script.onload = function() {
                    Loader.initKeycloak(keycloakSettings);
                };
                
                script.onerror = script.onabort = function() {
                    console.error("Cannot load " + script.src);
                    Loader.startLoading();
                };
                                    
                document.head.appendChild(script);
            };

            /*
             * Initialize keycloak and load the IDE
             */
            this.initKeycloak = function(keycloakSettings) {
                function keycloakConfig() {
                  const theOidcProvider = keycloakSettings['che.keycloak.oidc_provider'];
                  if (!theOidcProvider) {
                      return {
                        url: keycloakSettings['che.keycloak.auth_server_url'],
                        realm: keycloakSettings['che.keycloak.realm'],
                        clientId: keycloakSettings['che.keycloak.client_id']
                      };
                    } else {
                      return {
                        oidcProvider: theOidcProvider,
                        clientId: keycloakSettings['che.keycloak.client_id']
                      };
                    }
                }
                keycloak = Keycloak(keycloakConfig());
                
                window['_keycloak'] = keycloak;
                
                var useNonce;
                if (typeof keycloakSettings['che.keycloak.use_nonce'] === 'string') {
                  useNonce = keycloakSettings['che.keycloak.use_nonce'].toLowerCase() === 'true';
                }
                
                window.sessionStorage.setItem('oidcIdeRedirectUrl', location.href);
                keycloak
                    .init({
                      onLoad: 'login-required',
                      checkLoginIframe: false,
                      useNonce: useNonce,
                      scope: 'email profile',
                      redirectUri: keycloakSettings['che.keycloak.redirect_url.ide']
                    })
                    .success(function(authenticated) {
                        Loader.startLoading();
                    })
                    .error(function () {
                        console.log('[Keycloak] Failed to initialize Keycloak');
                    });    
            };
            
            /*
             * Show loader and load compilation-mapping.txt file to determine which IDE JavaScript file will be loaded
             */
            this.startLoading = function() {
                // going to detect IDE 
                var wsKey = window.location.pathname.substr(1);
                var request = new XMLHttpRequest();
                request.open("GET", '/api/workspace/' + wsKey);
                if(keycloak) {
                    request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token);
                }
                request.send();
                request.onreadystatechange = function () {
                    if (this.readyState !== 4) {
                        return;
                    }
                    if (this.status !== 200) {
                        var response = JSON.parse(this.responseText);
                        errorMessage = 'Failed to get the workspace: "' + response.message;
                        return;
                    }
                    var workspace = JSON.parse(this.responseText);
                    var machines = workspace.runtime.machines;
                    for (var machineName in machines) {
                        var servers = machines[machineName].servers;
                        for (var serverId in servers) {
                            var attributes = servers[serverId].attributes;
                            if (attributes['type'] === 'ide') {
                                //Avoid redirection loop in case if gwt-ide loaded in the sidecar
                                if(servers[serverId].url.contains("gwt/ide/sidecar/entrypoint")){
                                    return;
                                }
                                var url = servers[serverId].url + location.search;
                                // Preconfigured IDE may use dedicated port. In this case Chrome browser fails
                                // with error net::ERR_CONNECTION_REFUSED. Timer helps to open the URL without errors.
                                setTimeout(function () {
                                    window.location.href = url;
                                }, 100);
                                return;
                            }
                        }
                    }                            
                };
                //GWT IDE Loading
                setTimeout(() => {
                    document.getElementById("ide-loader").style.opacity = 1;
                }, 1);

                setTimeout(() => {
                    window.parent.postMessage("show-ide", "*");
                }, 250);                

                var msg = "Cannot load compilation mappings";

                try {
                    var request = new XMLHttpRequest();

                    request.onerror = request.abort = function() {
                        console.error(msg);
                    };

                    request.onload = function() {
                        if (request.status == 200) {
                            Loader.determineIDEJavaScriptFileName(request.responseText);
                        } else {
                            console.error(msg);
                        }
                    };

                    var url = "/_app/compilation-mappings.txt?" + new Date().getTime();
                    request.open("GET", url, true);
                    request.send();
                } catch (e) {
                    console.error(msg, e);
                }
            };

            /*
             * Returns browser identifier for searching in compilation-mappings.txt
             *
             * Many years ago this method was copied from
             * https://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/rebind/UserAgentPropertyGenerator.java?r=9701
             */
            this.getBrowserIdentifier = function() {
                var userAgent = navigator.userAgent.toLowerCase();

                var makeVersion = function(result) {
                    return (parseInt(result[1]) * 1000) + parseInt(result[2]);
                };

                if (userAgent.includes('opera')) {
                    return 'opera';
                } else if (userAgent.includes('webkit')) {
                    return 'safari';
                } else if (userAgent.includes('msie')) {
                    if (document.documentMode >= 8) {
                        return 'ie8';
                    } else {
                        var result = /msie ([0-9]+)\\.([0-9]+)/.exec(userAgent);
                        if (result && result.length == 3) {
                            var v = makeVersion(result);
                            if (v >= 6000) {
                                return 'ie6';
                            }
                        }
                    }
                } else if (userAgent.includes('gecko')) {
                    return 'gecko1_8';
                }

                return 'unknown';
            };

            /*
             * Determine name of IDE JavaScript file
             */
            this.ideJavaScriptFileName = null;
            this.ideJavaScriptFileSize = 0;

            this.determineIDEJavaScriptFileName = function(compilationMappings) {
                try {
                    var browserIdentifier = Loader.getBrowserIdentifier();

                    var mappings = compilationMappings.split("\n");

                    var jsName = null;
                    var mobileUserAgent = null;
                    var userAgent = null;
                    var webideClientOS = null;

                    for (var i = 0; i < mappings.length; i++) {
                        if (mappings[i] === "") {
                            if (jsName && userAgent && browserIdentifier === userAgent.split(" ")[1]) {
                                Loader.ideJavaScriptFileName = jsName;
                                Loader.loadCompilationMappingsPropertiesFile();
                                return;
                            }

                            jsName = null;
                            mobileUserAgent = null;
                            userAgent = null;
                            webideClientOS = null;
                        }

                        if (mappings[i].endsWith(".cache.js")) {
                            jsName = mappings[i];
                        }

                        if (mappings[i].startsWith("mobile.user.agent ")) {
                            mobileUserAgent = mappings[i];
                        }

                        if (mappings[i].startsWith("user.agent ")) {
                            userAgent = mappings[i];
                        }

                        if (mappings[i].startsWith("webide.clientOs ")) {
                            webideClientOS = mappings[i];
                        }
                    }
                } catch (e) {
                    console.error(e);
                }

                // IDE java script file name is not determined or error has occurred. Inject `.nocache.js` script and that's it.
                Loader.injectIDEJavaScript();
            };

            /*
             * Load compilation-mappings.properties and determine content length of IDE java script.
             *
             * The file must be generated when compiling the IDE and have following format
             *    0D3AF216CC89C9E9AAAC979BDBD6B5DF.cache.js : 9490046
             *    8D20590E9A332A3AA0062EE99096933F.cache.js : 9493464
             */
            this.loadCompilationMappingsPropertiesFile = function() {
                var msg = "Unable to load compilation-mappings.properties";

                try {
                    var request = new XMLHttpRequest();

                    request.onerror = request.abort = function() {
                        console.error(msg);
                        Loader.injectIDEJavaScript();
                    };

                    request.onload = function() {
                        if (request.status == 200) {
                            Loader.determineIDEJavaScriptContentLength(request.responseText);
                        } else {
                            console.error(msg);
                            Loader.injectIDEJavaScript();
                        }
                    };

                    var url = "/_app/compilation-mappings.properties?" + new Date().getTime();
                    request.open("GET", url, true);
                    request.send();
                } catch (e) {
                    console.error(msg, e);
                    Loader.injectIDEJavaScript();
                }
            };

            /*
             * Determine IDE java script file size
             */
            this.determineIDEJavaScriptContentLength = function(compilationMappingsProperties) {
                var mappings = compilationMappingsProperties.split("\n");

                try {
                    for (var i = 0; i < mappings.length; i++) {
                        var parts = mappings[i].split(" : ");

                        if (parts[0] === Loader.ideJavaScriptFileName) {
                            Loader.ideJavaScriptFileSize = parseInt(parts[1]);
                            Loader.loadIDEJavaScript();
                        }
                    }
                } catch (e) {
                    console.error("Cannot determine size of IDE JavaScript", e);
                }
            };

            /*
             * Load content of IDE JavaScript
             */
            this.loadIDEJavaScript = function() {
                var msg = "Unable to load IDE JavaScript";

                try {
                    var request = new XMLHttpRequest();

                    request.onprogress = function(event) {
                        var percentComplete = event.loaded / Loader.ideJavaScriptFileSize * 100;
                        var rounded = Math.round(percentComplete);
                        if (rounded > 100) {
                            rounded = 100;
                        }

                        document.getElementById("ide-loader-progress-bar").style.width = rounded + "%";
                    };

                    request.onload = function() {
                        Loader.injectIDEJavaScript();
                    };

                    request.onerror = request.abort = function () {
                        console.error(msg);
                        Loader.injectIDEJavaScript();
                    };

                    request.open("GET", "/_app/" + Loader.ideJavaScriptFileName, true);
                    request.send();
                } catch (e) {
                    console.error(msg, e);
                    Loader.injectIDEJavaScript();
                }
            };

            /*
             * Injects IDE JavaScript
             */
            this.injectIDEJavaScript = function() {
                document.getElementById("ide-loader-progress-bar").style.width = "100%";

                var script = document.createElement("script");
                script.type = "text/javascript";
                script.language = "javascript";
                script.async = true;
                script.src = "/_app/_app.nocache.js";
                document.head.appendChild(script);
            };
        };

        setTimeout(() => {
            Loader.loadKeycloakSettings();
        }, 1);

    </script>

</head>

<body style="background-color: #21252b; transition: background-color 0.5s ease;">
<div id="ide-loader" style="position: fixed; width: 300px; height: 45px; left: 50%; top: 30%;
            margin-left: -150px; opacity: 0; transition: all 0.2s ease-in-out;">
    <div style="position: absolute; left: 0px; top: 0px; width: 300px; height: 30px;
          font-family: sans-serif; font-size: 14px;
          line-height: 30px; text-align: center; color: #a0a9b7;">Loading...</div>
    <div style="position: absolute; width: 300px; height: 15px; left: 0px; bottom: 0px;
            background-color: #202325; border: 1px solid #456594; box-sizing: border-box;">
        <div id="ide-loader-progress-bar" style="box-sizing: border-box; height: 100%; width: 0%;
            border: 1px solid #161819; background-color: #498fe1; transition: all 0.2s ease-in-out;" />
    </div>
</div>
</body>

</html>
